package cn.com.bluemoon.daps.model.core.upload;

import cn.com.bluemoon.daps.common.constant.DapConstant;
import cn.com.bluemoon.daps.common.enums.SysWarehouseLevel;
import cn.com.bluemoon.daps.model.core.cache.ModelCacheHelper;
import cn.com.bluemoon.daps.model.service.DapDataModelTableService;
import cn.com.bluemoon.daps.standard.entity.DapSystemCategory;
import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.stream.Collectors;

import static cn.com.bluemoon.daps.model.core.file.excel.converter.MyTopicCategoryStringStringConverter.TOPIC_SPLIT_CHAR;

/**
 * @author Jarod.Kong
 */
@Component
@Slf4j
public class DapModelExcelUpload {

    @Autowired
    private DapDataModelTableService tableService;

    public List<String> doValidate(String modelId, List<TableInfo> tableInfos) {
        List<String> errors = new ArrayList<>();
        // 表名重复（文档本身、数据库中）
        Set<String> repeatTbNames = isRepeatDocTbName(tableInfos);
        if (repeatTbNames.size() > 0) {
            errors.add(String.format("导入文件中物理模型名[%s]存在重复，请检查！", String.join(",", repeatTbNames)));
        }
        List<String> repeatTableName = isRepeatDbTbName(modelId, tableInfos);
        if (!repeatTableName.isEmpty()) {
            errors.add(String.format("物理模型名[%s]已存在模型，请检查！", String.join(",", repeatTableName)));
        }
        // 表字段名重复（文档本身）
        for (TableInfo tableInfo : tableInfos) {
            List<TableInfo.FieldInfo> fieldInfos = tableInfo.getFieldInfos();
            Set<String> repeatFieldNames = isRepeatFieldName(fieldInfos);
            if (repeatFieldNames.size() > 0) {
                errors.add(String.format("物理模型%s存在重复字段英文名[%s]，请检查！", tableInfo.getTable().getName().getValue(),
                        String.join(",", repeatFieldNames)));
            }
        }

        try {
            for (TableInfo tableInfo : tableInfos) {
                doValidateTableInfo(errors, tableInfo);
                doValidateTableFieldInfo(errors, tableInfo);
            }
        } finally {
            ModelCacheHelper.clearTopicCache();
        }
        return errors;
    }

    private void doValidateTableInfo(List<String> errors, TableInfo tableInfo) {
        TableInfo.Table table = tableInfo.getTable();
        if (StringUtils.isEmpty(table.getName().getValue())) {
            errors.add(formatWithPosition("物理模型不可为空", table, table.getName()));
        }
        if (StringUtils.isEmpty(table.getComment())) {
            errors.add(formatWithPosition("逻辑模型不可为空", table, table.getComment()));
        }
        if (StringUtils.isEmpty(table.getTopicCategory())) {
            errors.add(formatWithPosition("主题分类不可为空", table, table.getTopicCategory()));
        }
        // 长度校验
        if (table.getName().getValue().length() > 64) {
            errors.add(formatWithPosition("物理模型不大于64", table, table.getName()));
        }
        if (table.getComment().getValue().length() > 2048) {
            errors.add(formatWithPosition("逻辑模型不大于2048", table, table.getComment()));
        }
        if (table.getDesc().getValueOrDefault("").length() > 500) {
            errors.add(formatWithPosition("表业务描述不大于500", table, table.getDesc()));
        }

        boolean matchDw = Arrays.stream(SysWarehouseLevel.values())
                .anyMatch(dw -> getTableDataWarehouseByTableName(table.getName().getValue()) != null);
        if (!matchDw) {
            errors.add(formatWithPosition("物理模型前缀校验不通过", table, table.getName()));
        }
        String topicCategory = table.getTopicCategory().getValue();
        final String[] topicsArr = topicCategory.split("/");
        if (topicsArr.length < 2) {
            log.info("主题分类，至少指定主题和一级分类");
            errors.add(formatWithPosition("主题分类-至少指定主题和一级分类", table, table.getTopicCategory()));
        }
        final String topic = StrUtil.trim(topicsArr[0]);
        final String category1 = StrUtil.trim(topicsArr[1]);
        List<String> topicIds = new ArrayList<>(topicsArr.length);
        // 初始化一次
        Map<String, List<DapSystemCategory>> topicAndCategories = ModelCacheHelper.getTopicAndCategoriesByCache();
        List<DapSystemCategory> topic2s = topicAndCategories.get(DapConstant.DapStandard.SYSTEM_TOPIC2_NAME);
        List<DapSystemCategory> categories1 = topicAndCategories.get(DapConstant.DapStandard.SYSTEM_CATEGORY1_NAME);
        Optional<DapSystemCategory> topicObj = topic2s.stream()
                .filter(o -> o.getCodeName().equalsIgnoreCase(topic)).findAny();
        if (!topicObj.isPresent()) {
            log.error("主题分类-主题匹配失败");
            errors.add(formatWithPosition("主题分类-主题匹配失败", table, table.getTopicCategory()));
            return;
        }
        String topicId = topicObj.get().getId();
        topicIds.add(topicId);
        Optional<DapSystemCategory> category1Obj = categories1.stream()
                .filter(o -> o.getParentId().equals(topicId))
                .filter(o -> o.getCodeName().equalsIgnoreCase(category1)).findFirst();
        if (!category1Obj.isPresent()) {
            log.error("主题分类，列值与系统一级分类匹配失败");
            errors.add(formatWithPosition("主题分类-一级分类匹配失败", table, table.getTopicCategory()));
            return;
        }
        topicIds.add(category1Obj.get().getId());
        log.debug("校验通过，翻译到的主题[{}]=[{}]", topicCategory, String.join(TOPIC_SPLIT_CHAR, topicIds));
        table.getTopicCategory().setVirtualValue(String.join(",", topicIds));
    }


    private void doValidateTableFieldInfo(List<String> errors, TableInfo tableInfo) {
        // field
        for (TableInfo.FieldInfo fieldInfo : tableInfo.getFieldInfos()) {
            if (StringUtils.isEmpty(fieldInfo.getName().getValue())) {
                errors.add(formatWithPosition("字段名称不可为空", fieldInfo, fieldInfo.getName()));
            }
            if (StringUtils.isEmpty(fieldInfo.getComment().getValue())) {
                errors.add(formatWithPosition("字段英文名不可为空", fieldInfo, fieldInfo.getComment()));
            }
            if (StringUtils.isEmpty(fieldInfo.getType().getValue())) {
                errors.add(formatWithPosition("字段类型不可为空", fieldInfo, fieldInfo.getType()));
            }
            // 字段长度校验
            if (fieldInfo.getName().getValue().length() > 64) {
                errors.add(formatWithPosition("字段英文名不大于64", fieldInfo, fieldInfo.getName()));
            }
            if (fieldInfo.getComment().getValue().length() > 1024) {
                errors.add(formatWithPosition("字段名称不大于1024", fieldInfo, fieldInfo.getComment()));
            }
            if (fieldInfo.getDesc().getValueOrDefault("").length() > 500) {
                errors.add(formatWithPosition("备注不大于500", fieldInfo, fieldInfo.getDesc()));
            }

        }
    }

    private Set<String> isRepeatFieldName(List<TableInfo.FieldInfo> fieldInfos) {
        Set<TableInfo.Cell> fNames = new HashSet<>();
        Set<String> repeatFNames = new HashSet<>();
        for (TableInfo.FieldInfo t : fieldInfos) {
            if (t.getName() == null){
                continue;
            }
            if (fNames.contains(t.getName())) {
                repeatFNames.add(t.getName().getValue());
            } else {
                fNames.add(t.getName());
            }
        }
        return repeatFNames;
    }

    private List<String> isRepeatDbTbName(String modelId, List<TableInfo> tableInfos) {
        List<String> tableNames = tableInfos.stream().filter(t -> t.getTable() != null && t.getTable().getName() != null)
                .map(t -> t.getTable().getName().getValue()).distinct().collect(Collectors.toList());
        List<String> repeatTableName = tableService.getRepeatTableName(tableNames, modelId);
        return repeatTableName;
    }

    private Set<String> isRepeatDocTbName(List<TableInfo> tableInfos) {
        Set<String> repeatTableNames = new HashSet<>();
        Set<TableInfo.Cell> tableNames = new HashSet<>();
        for (TableInfo t : tableInfos) {
            if (t.getTable() == null){
                continue;
            }
            if (tableNames.contains(t.getTable().getName())) {
                repeatTableNames.add(t.getTable().getName().getValue());
            } else {
                tableNames.add(t.getTable().getName());
            }
        }
        return repeatTableNames;
    }


    public String getTableDataWarehouseByTableName(String tableName) {
        if (tableName == null) {
            return null;
        }
        String tbLowerCase = tableName.toLowerCase();
        String dataWarehouse = null;
        if (tbLowerCase.startsWith("o_")) {
            dataWarehouse = "ods";
        } else if (tbLowerCase.startsWith("d_")) {
            dataWarehouse = "dwd";
        } else if (tbLowerCase.startsWith("s_")) {
            dataWarehouse = "dws";
        } else if (tbLowerCase.startsWith("dim_")) {
            dataWarehouse = "dim";
        } else if (tbLowerCase.startsWith("res_")) {
            // res->ad 而不是res
            dataWarehouse = "ads";
        }
        return dataWarehouse;
    }

    private String formatWithPosition(String formatStr, TableInfo.Position rowIndexPos, TableInfo.Position columnIndexPos) {
        return String.format("第%d行和第%d列-%s", rowIndexPos.getRowIndex() + 1, columnIndexPos.getColumnIndex() + 1, formatStr);
    }
}
